技术解读:现代化工具链在大规模 C++ 项目中的运用 | 龙蜥技术
编者按:C++ 语言与编译器一直都在持续演进,出现了许多令人振奋的新特性,同时还有许多新特性在孵化阶。除此之外,还有许多小更改以提高运行效率与编程效率。本文整理自全球 C++ 及系统软件技术大会上的精彩分享,接下来由作者带我们了解 C++ 项目的实践工作等具体内容,全文整理如下:
介绍
项目高度自治 – 自主决定编译器版本、语言标准 高度业务导向 – 少关注、不关注编译器和语言标准
先发劣势 – 丧失应用新技术、新特性的能力 沉疴难起 – 编译器版本、语言标准、库依赖被锁死
修复更严格编译器检查的问题
修复不同编译器行为差异的问题
修复语言标准、编译器行为改变的问题 – 完善测试
二进制依赖、ABI兼容问题 – 全源码编译/服务化
性能压测、调优
业务本身10+仓库;三方依赖50+,其中大部分源代码依赖,部分二进制依赖。
二进制依赖、ABI兼容问题 – 0.5人月;编译器切换、CI、CD – 1.5人月;性能分析调优 – 1人月。
二方/三方依赖 30+,二进制依赖。
编译器切换改造 – 2 人月;性能压测调优 – 1 人月。
二方/三方依赖 100+,多为二进制依赖。
二进制依赖、ABI 兼容问题 – 预估 2 人年。
更短的编译时间
更好的运行时性能
更好的编译、静态、运行时检查
更多优化技术 – ThinLTO、AutoFDO、Bolt 等
更新的语言特性支持 – C++20 协程、C++20 Module 等
持续性更新升级 – 良性循环
优化技术简介
ThinLTO
AutoFDO
同步接入:
首先编译一个 AutoFDO 不参与的二进制版本。
在 benchmark 环境下运行当前二进制并使用perf采集数据。
使用 AutoFDO 再次构建一个二进制版本,此二进制为最终发布版本。
异步接入:
在客户线上机器进行周期性采集,将采集数据进行合并和保存。
构建新版本的时候将对应的数据文件下载, 并参与当前版本的编译。
Bolt
BasicBlock Reordering Function Reordering ...
语言特性
Coroutines
协程是一个可挂起的函数。
支持以同步方式写异步代码。
C++20 协程是无栈协程。在语义层面不保存调用上下文信息。
对比有栈协程
两个数量级的切换效率提升。
更好的执行 & 切换效率。
对比 Callback
更简洁的编程模式,避免 Callback hell。
uint64_t ReadSync(std::vector<File> Inputs) {
uint64_t read_size = 0;
for (auto &&Input : Inputs)
read_size += ReadImplSync(Input);
return read_size;
}
template <RangeT Range, Callable Lambda>
future<void> do_for_each(Range, Lambda); // We need introduce another API.
future<uint64_t> ReadAsync(vector<File> Inputs) {
auto read_size = std::make_shared<uint64_t>(0); // We need introduce shared_ptr.
return do_for_each(Inputs, // Otherwise read_size would be
[read_size] (auto &&Input){ // released after ReadAsync ends.
return ReadImplAsync(Input).then([read_size](auto &&size){
*read_size += size;
return make_ready_future();
});
})
.then([read_size] { return make_ready_future<uint64_t>(*read_size); });
}
Lazy<uint64_t> ReadCoro(std::vector<File> Inputs) {
uint64_t read_size = 0;
for (auto &&Input : Inputs)
read_size += co_await ReadImplCoro(Input);
co_return read_size;
}
C++20 协程是无栈协程,需要编译器介入才能实现。
判定协程并搜索相关组件。(Frontend Semantic Analysis)
生成代码。(Frontend Code Generation)
生成、优化、维护协程桢。(Middle-end)
C++20 协程只设计了基本语法,并没有加入协程库。
C++20 协程的目标用户是协程库作者。
其他用户应通过协程库使用协程。
GCC
与社区合作进行协程的支持。
GCC-10 是第一个支持 C++ 协程特性的 GCC 编译器。
仅支持,无优化。
Clang/LLVM
与 Clang/LLVM 社区合作完善 C++ 协程。
改善&优化:对称变换、协程逃逸分析和CoroElide优化,协程帧优化(Frame reduction),完善协程调试能力、尾调用优化、Coro Return Value Optimization等。
在 Clang/LLVM14 中,coroutine 移出了 experimental namespace。
Maintaining
async_simple
设计借鉴了 folly 库协程模块。
轻量级。
包含有栈协程、无栈协程以及 Future/Promise 等异步组件。 从真实需求出发。
与调度器解藕,用户可以选择合适自己的调度器。
经受了工业级 Workload 的考验。
开源于:https://github.com/alibaba/async_simple
业务1(1M Loc、35w core)
原先为同步逻辑
协程化后 Latency 下降 30%
超时查询数量大幅下降甚至清零
业务2(7M Loc)
原先为异步逻辑
协程化后 Latency 下降 8%
业务3(100K Loc、2.7w core)
原先为同步逻辑
协程化后 qps 提升 10 倍以上性能
Modules
降低复杂度与出错的机会
更好的封装性
更快的编译速度
#include "a.h"
#include "b.h"
// another file
#include "b.h
#include "a.h"
import a;
import b;
import b;
import a;
namespace asio {
#if defined(ASIO_HAS_STD_STRING_VIEW)
using std::basic_string_view;
using std::string_view;
#elif defined(ASIO_HAS_STD_EXPERIMENTAL_STRING_VIEW)
using std::experimental::basic_string_view;
using std::experimental::string_view;
#endif // defined(ASIO_HAS_STD_EXPERIMENTAL_STRING_VIEW)
} // namespace asio
# define ASIO_STRING_VIEW_PARAM asio::string_view
#else // defined(ASIO_HAS_STRING_VIEW)
# define ASIO_STRING_VIEW_PARAM const std::string&
#endif // defined(ASIO_HAS_STRING_VIEW)
编译器初步开发完成
支持 std modules
优先内部应用
已在 Clang15 中发布
探索编译器与构建系统交互 (ing)
总结
更短的编译时间
更好的运行时性能
更好的编译、静态、运行时检查
更多优化技术 – ThinLTO、AutoFDO、Bolt 等
更新的语言特性支持 – C++20 协程、C++20 Module 等
持续性更新升级 – 良性循环
https://think-async.com/Asio/asio-1.22.1/doc/asio/index.html
https://cppcon.digital-medium.co.uk/session/2022/how-much-compilation-speedup-we-will-get-from-c-modules/。
加入龙蜥社群
3.再添重磅理事成员!麒麟软件、浪潮信息、中科曙光、新华三加入龙蜥
4.龙蜥开发者说:一人行快,众人行远!在龙蜥社区的日子,我想说这些 | 第 12 期5.赞!| 龙蜥及其理事分获“2022 OSCAR 尖峰开源社区及项目、尖峰开源人物”奖项